from numpy import double
from GeomAlgo import intersectTriangleZPlane, pointInPolygon
from LinkSegs_dorder import LinkSegs_dorder
from LinkSegs_dlook import LinkSegs_dlook
from IntersectStl_match import IntersectStl_match
from GeomBase import Point3D
from Polyline import Polyline
from StlModel import StlModel
from TopoSlicer import TopoSlicer
from layer import Layer
import struct
from IntersectStl_sweep import IntersectStl_sweep


def intersectStl_brutal(stlModel: StlModel, layerThk):
    layers = []
    xMin, xMax, yMin, yMax, zMin, zMax = stlModel.getBounds()
    z = zMin + layerThk
    while z < zMax:
        layer = Layer(z)
        for t in stlModel.triangles:
            seg = intersectTriangleZPlane(t, z)
            if seg is not None:
                layer.segments.append(seg)
        layers.append(layer)
        z += layerThk
    return layers


def linkSegs_brutal(segs):
    segs = list(segs)
    contours = []
    while len(segs) > 0:
        contour = Polyline()
        while len(segs) > 0:
            for seg in segs:
                if contour.appendSegment(seg):
                    segs.remove(seg)
                    break
            if contour.isClosed():
                break
        contours.append(contour)
    return contours


def writeSlcFile(layers, path):
    f = None
    try:
        f = open(path, "w+b")
        f.write(bytes("-SLCVER 2.0 -UNIT MM", encoding="utf-8"))
        f.write(bytes([0x0D, 0x0A, 0x1A]))
        f.write(bytes([0x00] * 256))
        f.write(struct.pack("b", 1))
        f.write(struct.pack("4f", 0, 0, 0, 0))
        for layer in layers:
            f.write(struct.pack("fI", layer.z, len(layer.contours)))
            for contour in layer.contours:
                f.write(struct.pack("2I", contour.count(), 0))
                for pt in contour.points:
                    f.write(struct.pack("2f", pt.x, pt.y))
        f.write(struct.pack("fI", layers[-1].z, 0xFFFFFFFF))
    except Exception as ex:
        print("writeSlcFile exception:", ex)
    finally:
        if f:
            f.close()


def readSlcFile(path):
    f, layers, i = None, [], 0
    try:
        f = open(path, "rb")
        data = f.read()
        while True:
            if data[i] == 0x0D and data[i + 1] == 0x0A and data[i + 2] == 0x1A:
                break
            i += 1
        i += 256 + 3
        channelCount = data[i]
        i += 1 + channelCount * 16
        while True:
            (z,) = struct.unpack("f", data[i : i + 4])
            i += 4
            (contourCount,) = struct.unpack("I", data[i : i + 4])
            i += 4
            if contourCount == 0xFFFFFFFF:
                break
            layer = Layer(z)
            for j in range(contourCount):
                (pointCount,) = struct.unpack("I", data[i : i + 4])
                i += 4
                (gapCount,) = struct.unpack("I", data[i : i + 4])
                i += 4
                contour = Polyline()
                for k in range(pointCount):
                    x, y = struct.unpack("2f", data[i : i + 8])
                    i += 8
                    contour.addPoint(Point3D(x, y, z))
                layer.contours.append(contour)
            layers.append(layer)
    except Exception as ex:
        print("readSlcFile exception:", ex)
    finally:
        if f:
            f.close()
        return layers


def adjustPolygonDirs(polygons):
    for i in range(len(polygons)):
        pt = polygons[i].startPoint()
        insideCount = 0
        for j in range(len(polygons)):
            if j == i:
                continue
            restPoly = polygons[j]
            if pointInPolygon(pt, restPoly) == 1:
                insideCount += 1
        if insideCount % 2 == 0:
            polygons[i].makeCCW()
        else:
            polygons[i].makeCW()


def intersectStl_sweep(stlModel, layerThk):
    return IntersectStl_sweep(stlModel, layerThk).layers


def intersectStl_match(stlModel: StlModel, layerThk):
    return IntersectStl_match(stlModel, layerThk).layers


def linkSegs_dorder(segs):
    return LinkSegs_dorder(segs).contours


def linkSegs_dlook(segs):
    return LinkSegs_dlook(segs).contours


def slice_topo(stlModel: StlModel, layerThk: float):
    return TopoSlicer(stlModel, layerThk).layers


def slice_combine(stlModel: StlModel, layerThk):
    layers = intersectStl_sweep(stlModel, layerThk)
    for layer in layers:
        layer.contours = linkSegs_dlook(layer.segments)
        layer.segments.clear()
        adjustPolygonDirs(layer.contours)
    return layers
